Graticules and limits read scales#495
Open
teunbrand wants to merge 9 commits into
Open
Conversation
SCALE lon/lat FROM [...] now overrides the data-derived bbox for viewport extent, and SCALE lon/lat SETTING breaks => [...] controls graticule line placement. Scale limits sit between data extent and PROJECT bounds in the precedence chain. Per-element fallback allows partial constraints (e.g. FROM (null, 40) constrains only the upper bound). OOB filtering is skipped for map position scales since their limits are in EPSG:4326 while data columns contain projected coordinates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Unit tests for scale_override_bbox (numeric limits, string limits, no explicit limits, mixed null/numeric) and scale_breaks (explicit array, numeric count ignored, absent, wrong aesthetic). Integration tests verify the full DuckDB path: scale limits constrain the projected bbox, and explicit breaks produce the expected number of graticule meridians. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
thomasp85
approved these changes
Jun 29, 2026
thomasp85
left a comment
Collaborator
There was a problem hiding this comment.
It would be nice if the graticule_breaks use would flow automatically from some sort of spatial transform on the scale but I can understand why that would be hard to detect for little gain
E.g. SCALE lat VIA spatial would give you a graticule breaks function and the spatial transform was default for lat and long aesthetics...
Identity transform whose `calculate_breaks` delegates to `graticule_breaks`, producing degree-aligned break positions (1°, 2°, 5°, 10°, 15°, 30°, etc.). Registered in the transform system with aliases "geographic" and "geo". Added to the Continuous scale type match arm so `VIA geographic` works. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extracts an `infer_transform` helper to unify the two duplicate transform-inference code paths. Map position scales (pos1/pos2) now receive the Geographic transform automatically when a map projection is active, so graticule breaks use degree-aligned logic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Propagates `&mut [Scale]` through the projection trait chain so that `build_graticule` can call `resolve()` on position scales with a synthetic ScaleDataContext from the geographic bbox. After resolution, breaks are read via `Scale::numeric_breaks()`. Scales are marked `resolved = true` so the later `resolve_scales` pass skips them. Also adds `ScaleDataContext::from_range()` constructor. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Position aesthetics already default to oob_keep, so the explicit map-position check in apply_scale_oob was unreachable. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests that `SCALE lon SETTING breaks => 4` produces exactly 4 graticule meridians (break count now resolved through geographic transform), and that map position scales are marked resolved after the projection step. Also ensures resolve_map_scale assigns Continuous type and Geographic transform when the scale has none (e.g. spatial-only queries where no column mapping creates position scales). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… clauses Create pos1/pos2 scales in resolve_scale_types_and_transforms when a map projection is active but no position scales exist (spatial layers only map "geometry"). Also fix .any() → .all() in numeric_range validation and strengthen the integration test to assert scale existence. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Collaborator
Author
|
I can see the attraction if having everything encapsulated in the scale system. I've made an effort to integrate the two systems better. I don't know if you want to give it another review @thomasp85, since the changes aren't negligible. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR aims to fix #492.
It hooks up the position scales to the map projections. The breaks hook into the graticule and the limits hook into the bbox.
Essentially the priority order of the bbox is now: data < scales < bounds setting. The procedure essentially this: if scale limits exist, then transform data bbox to lon/lat and apply scales independently, then retransform the scaled bbox back to the target CRS. You'll run into some limitations with heavily distorting projections, but at that point it'd be better to use the bounds setting anyway.